#pragma once
#include <memory>
#include <iostream>
#include <time.h>
#include <vector>
#include <assert.h>
#include <sstream>
#include "message.hpp"

/*
	日志消息类, 用于进行日志中间信息的存储：
	1、 日志的输出时间         可用于过滤日志
	2、 日志的等级            用于进行日志的过滤分析
	3、 源文件名称
	4、 源代码行号            用于定位出现错误的代码位置
	5、 线程ID               用于过滤出错的线程
	6、 日志主题消息
	7、 日志器名称            项目允许多日志器同时使用
*/

namespace CH
{
	// 抽象格式化子项基类
	class FormatItem
	{
	public:
		// 重命名
		using prt = std::shared_ptr<FormatItem>;					   // 等于typedef  std::shared_ptr<FormatItem>  pre
		virtual ~FormatItem(){};
		virtual void format(std::ostream &out, const LogMsg &msg) = 0; // 用于将LogMsg各个字段格式化到指定out流中
	};

	// 派生格式化子项子类 -- 消息
	class PayloadFormatItem : public FormatItem
	{
	public:
		virtual void format(std::ostream &out, const LogMsg &msg) override
		{
			out << msg._payload;
		}
	};

	// 派生格式化子项子类 -- 等级
	class LevelFormatItem : public FormatItem
	{
	public:
		virtual void format(std::ostream &out, const LogMsg &msg) override
		{
			out << LogLevel::tostring(msg._level);
		}
	};

	// 派生格式化子项子类 -- 文件名
	class FileNameFormatItem : public FormatItem
	{
	public:
		virtual void format(std::ostream &out, const LogMsg &msg)
		{
			out << msg._filename;
		}
	};

	// 派生格式化子项子类 -- 行号
	class LineFormatItem : public FormatItem
	{
	public:
		virtual void format(std::ostream &out, const LogMsg &msg)
		{
			out << msg._line;
		}
	};

	// 派生格式化子项子类 -- 日志器名字
	class LognameFormatItem : public FormatItem
	{
	public:
		virtual void format(std::ostream &out, const LogMsg &msg)
		{
			out << msg._logname;
		}
	};

	// 派生格式化子项子类 -- 线程id
	class TidFormatItem : public FormatItem
	{
	public:
		virtual void format(std::ostream &out, const LogMsg &msg)
		{
			out << msg._tid;
		}
	};

	// 派生格式化子项子类 -- 时间
	class TimeFormatItem : public FormatItem
	{
	public:
		TimeFormatItem(const std::string &format = "%H:%M:%S")
			: _format(format)
		{
		}
		virtual void format(std::ostream &out, const LogMsg &msg)
		{
			struct tm t;
			// 将时间戳，转换为各种时间存储在t结构体中
			localtime_r(&msg._ctime, &t);
			char ch[32];
			// 按照_format格式，从结构体t中，取时间，并将时间转化为字符串，放入ch中
			strftime(ch, 32, _format.c_str(), &t);
			out << ch;
		}

	private:
		std::string _format;
	};

	// 派生格式化子项子类 -- 其他
	class OtherFormatItem : public FormatItem
	{
	public:
		OtherFormatItem(const std::string &str) : _str(str) {}
		void format(std::ostream &out, const LogMsg &msg) override
		{
			out << _str;
		}

	private:
		std::string _str;
	};

	// 派生格式化子项子类 -- "\t"
	class TabFormatItem : public FormatItem
	{
	public:
		void format(std::ostream &out, const LogMsg &msg) override
		{
			out << '\t';
		}
	};

	// 派生格式化子项子类 -- "\n"
	class NewLineFormatItem : public FormatItem
	{
	public:
		void format(std::ostream &out, const LogMsg &msg) override
		{
			out << '\n';
		}
	};

	//格式化字符串
	class Formater
	{
	public:
		using prt=std::shared_ptr<Formater>;
		// 时间{年-月-日 时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志名称] 缩进 文件名:行号 缩进 消息换行
		Formater(const std::string &pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n")
			: _pattern(pattern)
		{
			assert(parsePattern()); // 判断输进来的字符串格式是否正确，如果错误，则没有必要进行了
		}

		//这里的参数，是由我们自己输入的，是一个给用户使用的接口
		void format(std::ostream& out,const LogMsg& msg)
		{
			//_items里面存储的是各个子类的实例化，该实例化已经在parsePattern函数中完成了
			for(auto item:_items){
				item->format(out,msg);
			}
		}

		/*  对msg进行格式化*/
		std::string format(const LogMsg &msg) {
			std::stringstream ss;
			format(ss, msg);
			return ss.str();
		}


	private:
		// 将子类实例化，并且返回一个智能指针，后面存储在vector中
		FormatItem::prt createItem(const std::string &key, const std::string &val)
		{
			if (key == "d")
				return std::make_shared<TimeFormatItem>(val);//这里传来的是子格式
			if (key == "t")
				return std::make_shared<TidFormatItem>();
			if (key == "c")
				return std::make_shared<LognameFormatItem>();
			if (key == "f")
				return std::make_shared<FileNameFormatItem>();
			if (key == "l")
				return std::make_shared<LineFormatItem>();
			if (key == "p")
				return std::make_shared<LevelFormatItem>();
			if (key == "T")
				return std::make_shared<TabFormatItem>();
			if (key == "m")
				return std::make_shared<PayloadFormatItem>();
			if (key == "n")
				return std::make_shared<NewLineFormatItem>();
			if (key.empty())
				return std::make_shared<OtherFormatItem>(val);//这里传来的是原始字符串
			std::cout<<"没有对应的格式化字符%"<<key<<endl;
			assert(false);   
			return FormatItem::prt();
		}

		//对格式化字符串进行解析
		bool parsePattern()
		{
			//  默认格式 "%d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n"
			std::vector<pair<std::string,std::string>> fmt_order;//对格式化字符，进行解析，为调用子类实例化做准备
			size_t pos=0;
			std::string key,val;
			while(pos<_pattern.size())
			{
				//首先找到%d,不是就是原始字符串，后将其放入val中
				if(_pattern[pos]!='%'){
					val.push_back(_pattern[pos]);
					pos+=1;
					continue;
				}

				//"asd%%ddddddac%d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n
				//防止将%作为字符串
				if(pos+1<_pattern.size()&&_pattern[pos+1]=='%'){
					val.push_back(_pattern[pos+1]);
					pos+=2;
					continue;
				}

				//走到这里，已经1找到了第一个格式化字符的%，所有将存储在val中的原始字符串，存入数组中
				//后调用其他子类进行实例化输出
				// 万一出现第一个自符就是格式化字符串，那么处理原始字符串的操作就会向数组插入{"",""}
                // 虽然不会产生错误但是便于逻辑理解，最好还是判断处理一下
				if (!val.empty()) {
					fmt_order.push_back(std::make_pair("", val));
				}
				val.clear();//清空

				//处理格式化字符串,代表原始字符串处理完毕
				pos+=1;
				if(pos==_pattern.size()){
					std::cout << "%之后没有对应的格式化字符" << std::endl;
                    return false; 
				}

				//将格式化字符，存入其中
				key=_pattern[pos];
				pos+=1;
				 // 此时pos指向格式化字符串后面的位置，判断是否有格式化子串
				if(pos<_pattern.size()&&_pattern[pos]=='{'){
					// 这时pos指向子规则的起始位置
					pos+=1;
					while(pos<_pattern[pos]&&_pattern[pos]!='}'){
						val.push_back(_pattern[pos]);
						pos+=1;
					}
					// 若走到了末尾，还没有找到},则说明格式是错误的，跳出循环
					if(pos==_pattern.size()){
						std::cout << "子规则{}匹配出错" << std::endl;
                        return false;
					}
					pos += 1; // 因为pos指向的是 } 位置，向后走一步就到了下一次处理的新位置
				}
				fmt_order.push_back(std::make_pair(key, val));
                key.clear(); val.clear();
			}
			// 2、根据解析得到的数据初始化格式子项数组成员,也就是实例化子类
			for (auto &it : fmt_order) {
				_items.push_back(createItem(it.first, it.second));
			} 
			return true;
		}

	private:
		const std::string _pattern;			 /// 用于存储字符串的格式
		std::vector<FormatItem::prt> _items; // 格式化子项数组
	};

}



